package com.phiture.erp.sale.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.phiture.erp.basic.api.pojo.resp.ErpMaterialResp;
import com.phiture.erp.basic.api.service.ErpAccountApi;
import com.phiture.erp.basic.api.service.ErpCustomerApi;
import com.phiture.erp.basic.api.service.ErpMaterialApi;
import com.phiture.erp.common.constants.ErpNoPrefixConstants;
import com.phiture.erp.common.enums.ErpAuditStatus;
import com.phiture.erp.common.utils.ErpNoGenerator;
import com.phiture.erp.sale.core.pojo.dto.ErpSaleOrderPageReqDTO;
import com.phiture.erp.sale.core.pojo.dto.ErpSaleOrderSaveReqDTO;
import com.phiture.erp.sale.core.service.ErpSaleOrderService;
import com.phiture.erp.sale.dal.entity.ErpSaleOrderDO;
import com.phiture.erp.sale.dal.entity.ErpSaleOrderItemDO;
import com.phiture.erp.sale.dal.mapper.ErpSaleOrderItemMapper;
import com.phiture.erp.sale.dal.mapper.ErpSaleOrderMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static com.phiture.erp.common.constants.ErrorCodeConstants.*;

/**
 * @author YH
 * @date 2025/4/8
 */

@Service
@RequiredArgsConstructor
public class ErpSaleOrderServiceImpl implements ErpSaleOrderService {

    private final ErpSaleOrderMapper saleOrderMapper;
    private final ErpSaleOrderItemMapper saleOrderItemMapper;
    private final AdminUserApi adminUserApi;
    private final ErpNoGenerator erpNoGenerator;
    private final ErpMaterialApi erpMaterialApi;
    private final ErpCustomerApi erpCustomerApi;
    private final ErpAccountApi erpAccountApi;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long createSaleOrder(ErpSaleOrderSaveReqDTO createReqDTO) {
        // 1.1 校验订单项的有效性
        List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(createReqDTO.getItems());
        // 1.2 校验客户
        erpCustomerApi.validateCustomer(createReqDTO.getCustomerId());
        // 1.3 校验结算账户
        if (createReqDTO.getAccountId() != null) {
            erpAccountApi.validateAccount(createReqDTO.getAccountId());
        }
        // 1.4 校验销售人员
        if (createReqDTO.getSaleUserId() != null) {
            adminUserApi.validateUser(createReqDTO.getSaleUserId());
        }
        // 1.5 生成订单号，并校验唯一性
        String orderNo = erpNoGenerator.generate(ErpNoPrefixConstants.SALE_ORDER_NO_PREFIX);
        validateSaleOrderNo(orderNo);

        // 2.1 插入订单
        ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqDTO, ErpSaleOrderDO.class, in -> in
                .setNo(orderNo).setStatus(ErpAuditStatus.PROCESS.getStatus()));
        calculateTotalPrice(saleOrder, saleOrderItems);
        saleOrderMapper.insert(saleOrder);
        // 2.2 插入订单项
        saleOrderItems.forEach(o -> o.setOrderId(saleOrder.getId()));
        saleOrderItemMapper.insertBatch(saleOrderItems);
        return saleOrder.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateSaleOrder(ErpSaleOrderSaveReqDTO updateReqDTO) {
        // 1.1 校验存在
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(updateReqDTO.getId());
        if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {
            throw exception(SALE_ORDER_UPDATE_FAIL_APPROVE, saleOrder.getNo());
        }
        // 1.2 校验客户
        erpCustomerApi.validateCustomer(updateReqDTO.getCustomerId());
        // 1.3 校验结算账户
        if (updateReqDTO.getAccountId() != null) {
            erpAccountApi.validateAccount(updateReqDTO.getAccountId());
        }
        // 1.4 校验销售人员
        if (updateReqDTO.getSaleUserId() != null) {
            adminUserApi.validateUser(updateReqDTO.getSaleUserId());
        }
        // 1.5 校验订单项的有效性
        List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(updateReqDTO.getItems());

        // 2.1 更新订单
        ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqDTO, ErpSaleOrderDO.class);
        calculateTotalPrice(updateObj, saleOrderItems);
        saleOrderMapper.updateById(updateObj);
        // 2.2 更新订单项
        updateSaleOrderItemList(updateReqDTO.getId(), saleOrderItems);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateSaleOrderStatus(Long id, Integer status) {
        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
        // 1.1 校验存在
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
        // 1.2 校验状态
        if (saleOrder.getStatus().equals(status)) {
            throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL);
        }
        // 1.3 存在销售出库单，无法反审核
        if (!approve && saleOrder.getOutCount().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_OUT);
        }
        // 1.4 存在销售退货单，无法反审核
        if (!approve && saleOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN);
        }

        // 2. 更新状态
        int updateCount = updateByIdAndStatus(id, saleOrder.getStatus(), new ErpSaleOrderDO().setStatus(status));
        if (updateCount == 0) {
            throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleOrderOutCount(Long id, Map<Long, BigDecimal> outCountMap) {
        List<ErpSaleOrderItemDO> orderItems = saleOrderItemMapper.selectListByOrderId(id);
        // 1. 更新每个销售订单项
        orderItems.forEach(item -> {
            BigDecimal outCount = outCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);
            if (item.getOutCount().equals(outCount)) {
                return;
            }
            if (outCount.compareTo(item.getCount()) > 0) {
                throw exception(SALE_ORDER_ITEM_OUT_FAIL_MATERIAL_EXCEED,
                        erpMaterialApi.getMaterial(item.getMaterialId()).getName(), item.getCount());
            }
            saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setOutCount(outCount));
        });
        // 2. 更新销售订单
        BigDecimal totalOutCount = getSumValue(outCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);
        saleOrderMapper.updateById(new ErpSaleOrderDO().setId(id).setOutCount(totalOutCount));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap) {
        List<ErpSaleOrderItemDO> orderItems = saleOrderItemMapper.selectListByOrderId(orderId);
        // 1. 更新每个销售订单项
        orderItems.forEach(item -> {
            BigDecimal returnCount = returnCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);
            if (item.getReturnCount().equals(returnCount)) {
                return;
            }
            if (returnCount.compareTo(item.getOutCount()) > 0) {
                throw exception(SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED,
                        erpMaterialApi.getMaterial(item.getMaterialId()).getName(), item.getOutCount());
            }
            saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setReturnCount(returnCount));
        });
        // 2. 更新销售订单
        BigDecimal totalReturnCount = getSumValue(returnCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);
        saleOrderMapper.updateById(new ErpSaleOrderDO().setId(orderId).setReturnCount(totalReturnCount));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSaleOrder(List<Long> ids) {
        // 1. 校验不处于已审批
        List<ErpSaleOrderDO> saleOrders = saleOrderMapper.selectBatchIds(ids);
        if (CollUtil.isEmpty(saleOrders)) {
            return;
        }
        saleOrders.forEach(saleOrder -> {
            if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {
                throw exception(SALE_ORDER_DELETE_FAIL_APPROVE, saleOrder.getNo());
            }
        });

        // 2. 遍历删除，并记录操作日志
        saleOrders.forEach(saleOrder -> {
            // 2.1 删除订单
            saleOrderMapper.deleteById(saleOrder.getId());
            // 2.2 删除订单项
            saleOrderItemMapper.deleteByOrderId(saleOrder.getId());
        });
    }

    @Override
    public ErpSaleOrderDO getSaleOrder(Long id) {
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
        // if (ObjectUtil.notEqual(saleOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
        //     throw exception(SALE_ORDER_NOT_APPROVE);
        // }
        return saleOrder;
    }

    @Override
    public ErpSaleOrderDO validateSaleOrder(Long id) {
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
        if (ObjectUtil.notEqual(saleOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
            throw exception(SALE_ORDER_NOT_APPROVE);
        }
        return saleOrder;
    }

    @Override
    public PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqDTO pageReqDTO) {
        MPJLambdaWrapperX<ErpSaleOrderDO> query = new MPJLambdaWrapperX<ErpSaleOrderDO>()
                .likeIfPresent(ErpSaleOrderDO::getNo, pageReqDTO.getNo())
                .eqIfPresent(ErpSaleOrderDO::getCustomerId, pageReqDTO.getCustomerId())
                .betweenIfPresent(ErpSaleOrderDO::getOrderTime, pageReqDTO.getOrderTime())
                .eqIfPresent(ErpSaleOrderDO::getStatus, pageReqDTO.getStatus())
                .eqIfPresent(ErpSaleOrderDO::getCustomerOrderNo, pageReqDTO.getCustomerOrderNo())
                .eqIfPresent(ErpSaleOrderDO::getCreator, pageReqDTO.getCreator())
                .orderByDesc(ErpSaleOrderDO::getId);
        // 入库状态。为什么需要 t. 的原因，是因为联表查询时，需要指定表名，不然会报 out_count 错误
        if (Objects.equals(pageReqDTO.getOutStatus(), ErpSaleOrderPageReqDTO.OUT_STATUS_NONE)) {
            query.eq(ErpSaleOrderDO::getOutCount, 0);
        } else if (Objects.equals(pageReqDTO.getOutStatus(), ErpSaleOrderPageReqDTO.OUT_STATUS_PART)) {
            query.gt(ErpSaleOrderDO::getOutCount, 0).apply("t.out_count < t.total_count");
        } else if (Objects.equals(pageReqDTO.getOutStatus(), ErpSaleOrderPageReqDTO.OUT_STATUS_ALL)) {
            query.apply("t.out_count = t.total_count");
        }
        // 退货状态
        if (Objects.equals(pageReqDTO.getReturnStatus(), ErpSaleOrderPageReqDTO.RETURN_STATUS_NONE)) {
            query.eq(ErpSaleOrderDO::getReturnCount, 0);
        } else if (Objects.equals(pageReqDTO.getReturnStatus(), ErpSaleOrderPageReqDTO.RETURN_STATUS_PART)) {
            query.gt(ErpSaleOrderDO::getReturnCount, 0).apply("t.return_count < t.total_count");
        } else if (Objects.equals(pageReqDTO.getReturnStatus(), ErpSaleOrderPageReqDTO.RETURN_STATUS_ALL)) {
            query.apply("t.return_count = t.total_count");
        }
        // 可销售出库
        if (Boolean.TRUE.equals(pageReqDTO.getOutEnable())) {
            query.eq(ErpSaleOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
                    .apply("t.out_count < t.total_count");
        }
        // 可销售退货
        if (Boolean.TRUE.equals(pageReqDTO.getReturnEnable())) {
            query.eq(ErpSaleOrderDO::getStatus, ErpAuditStatus.APPROVE.getStatus())
                    .apply("t.return_count < t.out_count");
        }
        if (pageReqDTO.getMaterialId() != null) {
            query.leftJoin(ErpSaleOrderItemDO.class, ErpSaleOrderItemDO::getOrderId, ErpSaleOrderDO::getId)
                    .eq(pageReqDTO.getMaterialId() != null, ErpSaleOrderItemDO::getMaterialId, pageReqDTO.getMaterialId())
                    .groupBy(ErpSaleOrderDO::getId); // 避免 1 对多查询，产生相同的 1
        }
        // MRP运算标记
        if (Boolean.FALSE.equals(pageReqDTO.getMrpFlag())) {
            query.eq(ErpSaleOrderDO::getMrpFlag, Boolean.FALSE);
        }

        PageResult<ErpSaleOrderDO> pageResult = saleOrderMapper.selectJoinPage(pageReqDTO, ErpSaleOrderDO.class, query);

        return pageResult;
    }

    @Override
    public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderId(Long orderId) {
        return saleOrderItemMapper.selectList(ErpSaleOrderItemDO::getOrderId, orderId);
    }

    @Override
    public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderIds(Collection<Long> orderIds) {
        if (CollUtil.isEmpty(orderIds)) {
            return Collections.emptyList();
        }
        return saleOrderItemMapper.selectListByOrderIds(orderIds);
    }

    private void validateSaleOrderNo(String orderNo){
        boolean exists = saleOrderMapper.exists(
                Wrappers.lambdaQuery(ErpSaleOrderDO.class)
                        .eq(ErpSaleOrderDO::getNo, orderNo)
                        .eq(ErpSaleOrderDO::getDeleted, Boolean.FALSE)
        );
        if(exists){
            throw exception(SALE_ORDER_NO_EXISTS);
        }
    }

    private List<ErpSaleOrderItemDO> validateSaleOrderItems(List<ErpSaleOrderSaveReqDTO.Item> list) {
        // 1. 校验物料存在
        List<ErpMaterialResp> materialList = erpMaterialApi.validMaterialList(convertSet(list, ErpSaleOrderSaveReqDTO.Item::getMaterialId));
        Map<Long, ErpMaterialResp> materialMap = convertMap(materialList, ErpMaterialResp::getId);
        // 2. 转化为 ErpSaleOrderItemDO 列表
        return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOrderItemDO.class, item -> {
            item.setMaterialUnitId(materialMap.get(item.getMaterialId()).getUnitId());
            item.setTotalPrice(MoneyUtils.priceMultiply(item.getMaterialPrice(), item.getCount()));
            if (item.getTotalPrice() == null) {
                return;
            }
            if (item.getTaxPercent() != null) {
                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
            }
        }));
    }

    private ErpSaleOrderDO validateSaleOrderExists(Long id) {
        ErpSaleOrderDO saleOrder = saleOrderMapper.selectById(id);
        if (saleOrder == null) {
            throw exception(SALE_ORDER_NOT_EXISTS);
        }
        return saleOrder;
    }

    private void updateSaleOrderItemList(Long id, List<ErpSaleOrderItemDO> newList) {
        // 第一步，对比新老数据，获得添加、修改、删除的列表
        List<ErpSaleOrderItemDO> oldList = saleOrderItemMapper.selectListByOrderId(id);
        List<List<ErpSaleOrderItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录
                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));

        // 第二步，批量添加、修改、删除
        if (CollUtil.isNotEmpty(diffList.get(0))) {
            diffList.get(0).forEach(o -> o.setOrderId(id));
            saleOrderItemMapper.insertBatch(diffList.get(0));
        }
        if (CollUtil.isNotEmpty(diffList.get(1))) {
            saleOrderItemMapper.updateBatch(diffList.get(1));
        }
        if (CollUtil.isNotEmpty(diffList.get(2))) {
            saleOrderItemMapper.deleteBatchIds(convertList(diffList.get(2), ErpSaleOrderItemDO::getId));
        }
    }

    private int updateByIdAndStatus(Long id, Integer status, ErpSaleOrderDO updateObj) {
        return saleOrderMapper.update(updateObj, new LambdaUpdateWrapper<ErpSaleOrderDO>()
                .eq(ErpSaleOrderDO::getId, id).eq(ErpSaleOrderDO::getStatus, status));
    }

    private void calculateTotalPrice(ErpSaleOrderDO saleOrder, List<ErpSaleOrderItemDO> saleOrderItems) {
        saleOrder.setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add));
        saleOrder.setTotalMaterialPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO));
        saleOrder.setTotalTaxPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));
        saleOrder.setTotalPrice(saleOrder.getTotalMaterialPrice().add(saleOrder.getTotalTaxPrice()));
        // 计算优惠价格
        if (saleOrder.getDiscountPercent() == null) {
            saleOrder.setDiscountPercent(BigDecimal.ZERO);
        }
        saleOrder.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleOrder.getTotalPrice(), saleOrder.getDiscountPercent()));
        saleOrder.setTotalPrice(saleOrder.getTotalPrice().subtract(saleOrder.getDiscountPrice()));
    }
}
